package parseador;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Stack;
import java.util.Vector;

import javax.swing.JTextArea;



import fuentes.AnalizadorLexico;
import fuentes.Mensajes;
import fuentes.Simbolo;
import fuentes.SimboloTerceto;
import fuentes.TablaConversiones;
import fuentes.TablaSimbolos;
import fuentes.Terceto;
import fuentes.TercetoIncompleto;

public class TareasParser {
	static public AnalizadorLexico lex;
	static public String tipo;
	static public Vector<String> controlAmbitos;
	static public Hashtable<String,Integer> controlDeclaraciones;
	static public Vector<String> vector;
	static public Integer nroT;
	static public TercetoIncompleto tercetoInc;
	static public Vector<Terceto> tercetos;
	static public Stack<SimboloTerceto> operandosTerceto;
	static public Stack<TercetoIncompleto> tInc;
	static public Vector<TercetoIncompleto> tIncompletos;
	static public Integer contadorEtiqueta;
	

	static public boolean asignableULONG;
	static public boolean asignableFLOAT;
	static public boolean asignacionNEG;
	
	static public int numAmbito=1;
	
	
	
	/**
	 * Este metodo se utiliza para los casos en los que se reconoce un variable con
	 * signo menos adelante. Por ejemplo:
	 * 		 a:- -d + 1;
	 * en ese caso lo correcto es generar un terceto de multiplicacion con d y -1,
	 * luego generar un terceto de suma con el resultado del terceto anterior y 1,
	 * y luego generar el terceto de asignacion con el terceto anterior y a:
	 * 
	 * 		1 ( * , d_1 , -1 )
	 * 		2 ( + , [1] , 1 )
	 * 		3 ( :- , a_1 , [2] )
	 * 
	 * Por este motivo se agrega -1 a la Tabla de Simbolos.
	 * @param string
	 * @param tipo
	 */
	public static void AgregarTablaSimbolos(String string,String tipo) {
		TablaSimbolos ts=TablaSimbolos.getInstance();
		Simbolo s=ts.getSimbolo(string);
		if (s==null)
		{
			if (string.contains("-"))
			{	
				String str = string.substring(1, string.length());
				Simbolo sim=ts.getSimbolo(str);
				if (sim!=null)
					if (sim.getCantOcurrencias()==1)
						ts.removerSimbolo(sim);
					else
						sim.decrementarOcurrencias();
						
				s=new Simbolo("CTE",string);
				s.setTipo(tipo);
				ts.AddSimbolo(s);
			}
		}
	}

	/**
	 * Este metodo retorna true si el token es un comparador o false de lo contrario.
	 * @param s
	 * @return boolean
	 */
	public static boolean esComparador(String s)
	{
		if ((s.equals("<>")) || (s.equals("=")) || (s.equals("<=")) || (s.equals(">=")) || (s.equals("<")) || (s.equals(">"))) 
			return true;
		return false;
	}

	
	/**
	 * Este metodo retorna los valores del parser que se corresponden con las
	 * palabras reservadas.
	 * @param s
	 * @return int
	 */
	public static int palabraReservada(String s)
	{
		if(s.equals("IF"))
		 {
	     return Parser.IF;} 
		else 
			if (s.equals("ELSE"))
		       return Parser.ELSE;
		else 
			if (s.equals("THEN"))
					  return Parser.THEN;
		else 
	        	 if (s.equals("DO"))
		         	    return Parser.DO;
		         
		         else if (s.equals("WHILE"))
			                  return Parser.WHILE;
			          
			          else if (s.equals("PRINT"))
				              return Parser.PRINT;
				            
				            else if (s.equals("FLOAT")|| s.equals("float"))
					                return Parser.FLOAT;
				                  
				                  else if (s.equals("ULONG") || s.equals("ulong"))
				                         return Parser.ULONG;
				                       
				                     
		return 0;
		
	}
	/**
	 * Este metodo verifica si una declaracion ya fue hecha previamente.
	 * @param tipo
	 * @param linea
	 */
	public static void verificarDeclaracion(String tipo,int linea)
	{	
		TablaSimbolos TS=TablaSimbolos.getInstance();
		Mensajes m=Mensajes.getInstance();
		Integer lin=new Integer(linea);
		for (int i = 0; i < vector.size(); i++) 
		{
			Simbolo s=TS.getSimbolo(vector.elementAt(i));
			if (!s.isDeclarado())
			{
				s.setTipo(tipo);
				s.declarar();
			}
			else
			{
				m.guardarMensaje("Linea: "+lin.toString()+" Error declaracion previamente hecha.");
			}
		}
		vector.clear();
	}
	/**
	 * Este metodo realiza el procedimiento de name mangling para las variables
	 * declaradas en los ambitos.
	 */
	public static void verificarAmbitos()
	{

		TablaSimbolos TS=TablaSimbolos.getInstance();
		
		for (int i = 0; i < controlAmbitos.size(); i++) 
		{
			Simbolo s=TS.getSimbolo(controlAmbitos.elementAt(i));
			if (s.isDeclarado())
			{
				String nombre = s.getNombre();
				String tipo = s.getTipo();
				TS.removerSimbolo(s);
				s = new Simbolo(nombre, controlAmbitos.elementAt(i)+"_"
								+(new Integer(numAmbito)).toString()+"_1", tipo);
				s.declarar();
				TS.AddSimbolo(s);
			}
			
		}
		//numAmbito++;
		controlAmbitos.clear();
	}
	/**
	 * Este metodo realiza el procedimiento de name mangling para
	 * las variables globales.
	 */
	public static void verificarDeclGlobal()
	{
		TablaSimbolos TS=TablaSimbolos.getInstance();
		for (int i = 0; i < controlAmbitos.size(); i++) 
		{
			Simbolo s=TS.getSimbolo(controlAmbitos.elementAt(i));
			if (s.isDeclarado())
			{
				String nombre = s.getNombre();
				String tipo = s.getTipo();
				TS.removerSimbolo(s);
				s = new Simbolo(nombre, controlAmbitos.elementAt(i)+"_1", tipo);
				s.declarar();
				TS.AddSimbolo(s);
			}
		}
		controlAmbitos.clear();
		
	}
	/**
	 * Este metodo verifica las declaraciones de variables en los ambitos
	 * con el objetivo de diferenciar las variables utilizados en dicho ambito
	 * en base a si son globales o si fueron definidas en ese lugar. Se se detecta
	 * una sentencia dentro del ambito en la cual se utiliza una variable, se verifica que
	 * dicha variable este definida en el ambito, sino se verifica que sea global y si no 
	 * es asi si informa el error.
	 */
	public static void verificarDeclaracionAmbitos(){
		
		TablaSimbolos TS=TablaSimbolos.getInstance();
		Mensajes m=Mensajes.getInstance();
		
		Enumeration<String> keys = controlDeclaraciones.keys();

		while (keys.hasMoreElements())
		{
			String clave = keys.nextElement();
			Simbolo aVerificar=TS.getSimbolo(clave+"_"
					+(new Integer(numAmbito)).toString()+"_1");
			if (aVerificar==null)
			{
				Simbolo s=TS.getSimbolo(clave+"_1");
				if (s!=null)
				{
					Simbolo aEliminar=TS.getSimbolo(clave);
					TS.removerSimbolo(aEliminar);
				}
				else m.guardarMensaje("Linea: "+controlDeclaraciones.get(clave).toString()+" Error variable no declarada.");
				
			}
			else
			{
				Simbolo aEliminar=TS.getSimbolo(clave);
				TS.removerSimbolo(aEliminar);
			}
		}
		
		controlDeclaraciones.clear();
	}

	/**
	 * Este metodo retorna el terceto que cuyo nro es pasado por parametro.
	 * @param nroT2
	 * @return Terceto
	 */
	public static Terceto obtenerTerceto(Integer nroT2) {
		//Este metodo devuelve el terceto cuyo numero es igual al
		//pasado por parametro. Si no existe retorna null.
		Terceto aRetornar=null;
		int i = 0;
		boolean encontrado = false;
		while (i<tercetos.size() && !encontrado)
		{
			if (tercetos.elementAt(i).getNroT().equals(nroT2))
			{
				aRetornar = tercetos.elementAt(i);
				encontrado = true;
			}else
			i++;
		}
		return aRetornar;
	}

	/**
	 * Este metodo genera un terceto con los valores pasados por parametro y lo agrega
	 * a un vector.
	 * @param operador
	 * @param operando1
	 * @param operando2
	 * @return el numero de terceto siguiente
	 */
	public static Integer generarTerceto(String operador, SimboloTerceto operando1,
			SimboloTerceto operando2) {
		//Este metodo genera un terceto con los valores pasados por parametro.
		//Retorna el numero del proximo terceto.
		
		Terceto aAgregar = new Terceto(operador, operando1, operando2, nroT);
		completarTerceto(aAgregar);
		tercetos.add(aAgregar);
				
		nroT++;
		
		return nroT;
	}

	/**
	 * Este metodo se utiliza para realizar el Back Patching correspondiente a las
	 * sentencias de control. Completa los tercetos de bifurcacion.
	 * @param aCompletar
	 * @param nroT2
	 */
	public static void completarTerceto(Terceto faltante) {
		
		Vector<TercetoIncompleto> aEliminar = new Vector<TercetoIncompleto>();
		for (int i = 0; i < tIncompletos.size(); i++) 
		{
			if (tIncompletos.elementAt(i).getNroT().equals(faltante.getNroT()))
				if (tIncompletos.elementAt(i).getIncompleto().getOperador().equals("BF"))
				{
					tIncompletos.elementAt(i).getIncompleto().setOperando2(faltante);
					aEliminar.add(tIncompletos.elementAt(i));
				}
				else 
				if (tIncompletos.elementAt(i).getIncompleto().getOperador().equals("BI"))
				{
					tIncompletos.elementAt(i).getIncompleto().setOperando1(faltante);
					aEliminar.add(tIncompletos.elementAt(i));
				}
				else 
				if (tIncompletos.elementAt(i).getIncompleto().getOperador().equals("while"))
				{
					tIncompletos.elementAt(i).setIncompleto(faltante);
					aEliminar.add(tIncompletos.elementAt(i));
				}
		}
		for (int i = 0; i < aEliminar.size(); i++)
		{	
			tIncompletos.remove(aEliminar.elementAt(i));	
		}
		
		
	}
	/**
	 * Este metodo muestra por pantalla los tercetos generados.
	 * @param t
	 */
	public static void mostrarTercetos(JTextArea t){
		for (int i = 0; i < tercetos.size(); i++) 
		{
			t.append(tercetos.elementAt(i).getTerceto()+"\n");
		}
	}
	
	/**
	 * Este metodo retorna el tipo del operador.
	 * @param sval
	 * @return tipo de la variable/cte
	 */
	public static String obtenerTipo(String sval) {
		if (!sval.equals("error"))
		{
			TablaSimbolos ts=TablaSimbolos.getInstance();
			Simbolo s=ts.getSimbolo(sval);
			
			return s.getTipo();
		}
		else
			return "error";
	}
	public static Simbolo obtenerTexto(String texto){
		TablaSimbolos TS=TablaSimbolos.getInstance();
		
		Simbolo aObtener=TS.getSimbolo(texto);
		if (aObtener==null)
		{
			return new Simbolo("TEXTO","error","error");
		}
		else
			return aObtener;
	}
	
	/**
	 * Este metodo retorna el nombre de la variable referenciada en la Tabla de Simbolos.
	 * Si la variable no existe, se retorna como variable "error".
	 * @param var
	 * @return Nombre de la variable
	 */
	public static Simbolo obtenerVariable(String var){
		
		TablaSimbolos TS=TablaSimbolos.getInstance();
		
		Simbolo aObtener=TS.getSimbolo(var+"_"
				+(new Integer(numAmbito)).toString()+"_1");
		if (aObtener==null)
		{
			Simbolo s=TS.getSimbolo(var+"_1");
			if (s!=null)
			{
				return s;
			}
			else
				return new Simbolo("ID","error","error");
		}
		else
			return aObtener;		
	}
	
	/**
	 * Este metodo retorna un simbolo que se corresponde con
	 * el de la cte pasado por parametro.
	 * En caso de que la cte no haya sido declarada, se retorna un Simbolo
	 * con el valor "error" y con tipo "error".
	 * @param cte
	 * @return Simbolo
	 */
	public static Simbolo obtenerCte(String cte){
		
		TablaSimbolos TS=TablaSimbolos.getInstance();
		
		Simbolo aObtener=TS.getSimbolo(cte);
		//System.out.println("CTE obtenida: "+aObtener.getValor());
		//System.out.println("CTE tipo: "+aObtener.getTipo());
		if (aObtener==null)
		{
			return new Simbolo("CTE","error","error");
		}
		else
			return aObtener;
	}

	/**
	 * Este metodo verifica si es posible realizar la asignacion.
	 * De ser posible realiza el terceto de conversion de tipo si fuera necesario
	 * y luego el terceto de asignacion. De lo contrario, guarda el error de conversion
	 * de tipo.
	 * @param op1
	 * @param op2
	 * @param linea
	 */
	public static void verificarAsignacion(SimboloTerceto op1, SimboloTerceto op2, int linea){
		if (!op1.getTipo().equals(op2.getTipo()) && (!op1.getTipo().equals("error")) && (!op2.getTipo().equals("error")))
			if (op1.getTipo().equals("float")||op1.getTipo().equals("FLOAT"))
			{
				//GENERAR TERCETO DE CONVERSION IMPLICITA (ULTOF) PARA OP2 A FLOAT
				nroT = generarTerceto("ultof", op2,new Simbolo("null","null"));
				obtenerTerceto(new Integer(nroT.intValue()-1)).setTipo("float");
				nroT = generarTerceto(":-",op1,obtenerTerceto(new Integer(nroT.intValue()-1)));
			}
			else{
				//MUESTRO ERROR "IMPOSIBLE CONVERTIR DE FLOAT A ULONG"
				Mensajes m=Mensajes.getInstance();
				nroT = generarTerceto(":-",op1,op2);
				m.guardarMensaje("Linea: "+new Integer(linea).toString()+" Imposible convertir FLOAT a ULONG.");
			}
		else
			nroT = generarTerceto(":-",op1,op2);
	}
	/**
	 * Este metodo verifica si es necesario realizar alguna conversion de tipo,
	 * de ser cierto genera los tercetos correspondientes para la conversion de tipo.
	 * Finalmente genera el terceto de comparacion.
	 * @param operando
	 * @param op1
	 * @param op2
	 */
	public static void verificarComparacion(String operando, SimboloTerceto op1, SimboloTerceto op2){
		if ((!op1.getTipo().equals(op2.getTipo())) && (!op1.getTipo().equals("error")) && (!op2.getTipo().equals("error")))
		{
			if (op1.getTipo().equals("float") || op1.getTipo().equals("FLOAT"))
			{
				nroT = generarTerceto("ultof", op2,new Simbolo("null","null"));
				obtenerTerceto(new Integer(nroT.intValue()-1)).setTipo("float");
				nroT = generarTerceto(operando,op1,obtenerTerceto(new Integer(nroT.intValue()-1)));
			}
			else
			{
				nroT = generarTerceto("ultof", op1,new Simbolo("null","null"));
				obtenerTerceto(new Integer(nroT.intValue()-1)).setTipo("float");
				nroT = generarTerceto(operando,obtenerTerceto(new Integer(nroT.intValue()-1)),op2);
			}
		}
		else
		{
			nroT = generarTerceto(operando,op1,op2);
		}
	}

	/**
	 * Este metodo verifica si es necesario realizar alguna conversion implicita,
	 * de ser cierto, genera los tercetos correspondientes de conversiones de tipos.
	 * Luego genera el terceto correspondiente a la operacion aritmetica.
	 * 
	 * @param operador
	 * @param op1
	 * @param op2
	 * @return el tipo del resultado
	 */
	public static String generarConversionImplicita(String operador, SimboloTerceto op1, SimboloTerceto op2){
		
			//GENERAR TERCETOS DE CONVERSION IMLICITA PARA: + , - , * , /
			
			TablaConversiones TC = TablaConversiones.getInstance();
			Vector<String> conv = TC.obtenerConversion(operador, op1.getTipo(), op2.getTipo());
			SimboloTerceto opTerceto1 = op1;
			SimboloTerceto opTerceto2 = op2;
			for (int i = 0; i < conv.size(); i++) 
			{
				if (conv.elementAt(i).equals("op1"))
				{
					nroT = generarTerceto("ultof", op1,new Simbolo("null","null"));
					opTerceto1=obtenerTerceto(new Integer(nroT.intValue()-1));
					opTerceto1.setTipo("float");
				}
				else
				if (conv.elementAt(i).equals("op2"))
				{
					nroT = generarTerceto("ultof", op2,new Simbolo("null","null"));
					opTerceto2=obtenerTerceto(new Integer(nroT.intValue()-1));
					opTerceto2.setTipo("float");
				}
					
			}
			nroT = generarTerceto(operador, opTerceto1, opTerceto2);
			obtenerTerceto(new Integer(nroT.intValue()-1)).setTipo(TC.obtenerTipoResultado(operador, opTerceto1.getTipo(), opTerceto2.getTipo()));
			return TC.obtenerTipoResultado(operador, opTerceto1.getTipo(), opTerceto2.getTipo());

	}
}
